#include "jtimer.h"
#include <JXQCore/JThreadPool>
#include <JXQCore/JMutex>
#include <JXQCore/JDebug>
#include <condition_variable>

namespace JXQ
{

static JThreadPool gThreadPool(1000);

class J_DECL_HIDDEN JTimerPrivate
{
public:
	inline JTimerPrivate(JTimer *q_ptr) :
		q_ptr(q_ptr) {}

	~JTimerPrivate();

public:
	void start(uint32_t msecs);
	inline void stop();

public:
	std::atomic_int m_ref {1}; //计数器，记录谁在占用这个类
	std::atomic_bool m_isSingleShot {false};

	static int timerCount;
	static std::mutex timerCountMutex;

private:
	void run();
	inline void _start(uint32_t msecs);

private:
	JTimer *q_ptr;

	uint32_t m_msecs;
	std::mutex m_msecsMutex;

	std::atomic_bool m_quitFlag {true};
	std::condition_variable m_condition;

	std::mutex conditionMutex;
};

int JTimerPrivate::timerCount = 0;
std::mutex JTimerPrivate::timerCountMutex;

JTimerPrivate::~JTimerPrivate()
{
	stop();
}

void JTimerPrivate::start(uint32_t msecs)
{
	J_MUTEX_LOCKER(timerCountMutex);
	if( timerCount >= 1000 )
	{
		jError() << "JTimer: Too many timers (1000). no start";
		return ;
	}
	J_MUTEX_LOCKER_UNLOCK();

	if( m_quitFlag )
		_start(msecs);
	else
	{
		J_MUTEX_LOCKER(m_msecsMutex);
		if( m_msecs != msecs )
		{
			stop();
			_start(msecs);
		}
	}
}

void JTimerPrivate::stop()
{
	m_quitFlag = true;
	m_condition.notify_all();
}

void JTimerPrivate::run()
{
	for(;;)
	{
		std::unique_lock<std::mutex> locker(conditionMutex);
		m_condition.wait_for(locker, std::chrono::milliseconds(m_msecs));

		if( m_quitFlag )
			break;

		if( m_ref > 1 )
			q_ptr->timeout.send();

		if( m_isSingleShot )
		{
			m_quitFlag = true;
			break;
		}
	}

	if( not --m_ref )
		delete this;
}

inline void JTimerPrivate::_start(uint32_t msecs)
{
	m_msecs = msecs;
	m_quitFlag = false;
	++m_ref;

	gThreadPool.start(&JTimerPrivate::run, this);
}

/*----------------------------------------------------------------*/

JTimer::JTimer() :
	d_ptr(new JTimerPrivate(this))
{
	J_MUTEX_LOCKER(d_ptr->timerCountMutex);
	if( d_ptr->timerCount >= 1000 )
	{
		J_MUTEX_LOCKER_UNLOCK();
		jError() << "JTimer: Too many timers (1000). no start";
	}
	else
		++d_ptr->timerCount;
}

JTimer::~JTimer()
{
	J_MUTEX_LOCKER(d_ptr->timerCountMutex);
	--d_ptr->timerCount;

	d_ptr->stop();
	if( not --d_ptr->m_ref )
		delete d_ptr;
}

void JTimer::start(uint32_t msecs)
{
	d_ptr->start(msecs);
}

void JTimer::stop()
{
	d_ptr->stop();
}

void JTimer::setSingleShot(bool flag)
{
	d_ptr->m_isSingleShot = flag;
}

bool JTimer::isSingleShot() const
{
	return d_ptr->m_isSingleShot;
}

void JTimer::singleStart(uint32_t msecs, std::function<void()> &&callBack)
{
	auto timer = new JTimer();
	timer->timeout.subscribe([timer, callBack]()
	{
		delete timer;
		if( callBack != nullptr )
			callBack();
	});
	timer->start(msecs);
}

} //namespace JXQ
